/**
* @file event.cpp
*
* Copyright (c) Huawei Technologies Co., Ltd. 2019-2020. All Rights reserved.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

#include "acl/acl_rt.h"
#include "runtime/event.h"
#include "runtime/config.h"
#include "log_inner.h"
#include "error_codes_inner.h"
#include "toolchain/profiling_manager.h"
#include "toolchain/resource_statistics.h"

aclError aclrtCreateEvent(aclrtEvent *event)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtCreateEvent);
    ACL_ADD_APPLY_TOTAL_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_EVENT);
    ACL_LOG_INFO("start to execute aclrtCreateEvent");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(event);

    rtEvent_t rtEvent = nullptr;
    const rtError_t rtErr = rtEventCreate(&rtEvent);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("create event failed, runtime result = %d", static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }

    *event = static_cast<aclrtEvent>(rtEvent);
    ACL_LOG_INFO("successfully execute aclrtCreateEvent");
    ACL_ADD_APPLY_SUCCESS_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_EVENT);
    return ACL_SUCCESS;
}

aclError aclrtCreateEventWithFlag(aclrtEvent *event, uint32_t flag)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtCreateEventWithFlag);
    ACL_ADD_APPLY_TOTAL_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_EVENT);
    ACL_LOG_INFO("start to execute aclrtCreateEventWithFlag.");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(event);
    rtEvent_t rtEvent = nullptr;
    const rtError_t rtErr = rtEventCreateWithFlag(&rtEvent, flag);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("create event flag failed, runtime result = %d", static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    *event = static_cast<aclrtEvent>(rtEvent);
    ACL_LOG_INFO("successfully execute aclrtCreateEventWithFlag.");
    ACL_ADD_APPLY_SUCCESS_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_EVENT);
    return ACL_SUCCESS;
}

aclError aclrtCreateEventExWithFlag(aclrtEvent *event, uint32_t flag)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtCreateEventExWithFlag);
    ACL_ADD_APPLY_TOTAL_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_EVENT);
    ACL_LOG_INFO("start to execute aclrtCreateEventExWithFlag.");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(event);
    rtEvent_t rtEvent = nullptr;
    const rtError_t rtErr = rtEventCreateExWithFlag(&rtEvent, flag);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("create event ex with flag failed, runtime result = %d", static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    *event = static_cast<aclrtEvent>(rtEvent);
    ACL_LOG_INFO("successfully execute aclrtCreateEventExWithFlag.");
    ACL_ADD_APPLY_SUCCESS_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_EVENT);
    return ACL_SUCCESS;
}

aclError aclrtDestroyEvent(aclrtEvent event)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtDestroyEvent);
    ACL_ADD_RELEASE_TOTAL_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_EVENT);
    ACL_LOG_INFO("start to execute aclrtDestroyEvent");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(event);

    const rtError_t rtErr = rtEventDestroy(static_cast<rtEvent_t>(event));
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("destroy event failed, runtime result = %d", static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    ACL_LOG_INFO("successfully execute aclrtDestroyEvent");
    ACL_ADD_RELEASE_SUCCESS_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_EVENT);
    return ACL_SUCCESS;
}

aclError aclrtRecordEvent(aclrtEvent event, aclrtStream stream)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtRecordEvent);
    ACL_ADD_APPLY_TOTAL_COUNT(acl::ACL_STATISTICS_RECORD_RESET_EVENT);
    ACL_LOG_INFO("start to execute aclrtRecordEvent");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(event);

    const rtError_t rtErr = rtEventRecord(static_cast<rtEvent_t>(event), static_cast<rtStream_t>(stream));
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("record event failed, runtime result = %d", static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    ACL_LOG_INFO("successfully execute aclrtRecordEvent");
    ACL_ADD_APPLY_SUCCESS_COUNT(acl::ACL_STATISTICS_RECORD_RESET_EVENT);
    return ACL_SUCCESS;
}

aclError aclrtResetEvent(aclrtEvent event, aclrtStream stream)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtResetEvent);
    ACL_ADD_RELEASE_TOTAL_COUNT(acl::ACL_STATISTICS_RECORD_RESET_EVENT);
    ACL_LOG_INFO("start to execute aclrtResetEvent");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(event);

    const rtError_t rtErr = rtEventReset(static_cast<rtEvent_t>(event), static_cast<rtStream_t>(stream));
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("reset event failed, runtime result = %d", static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    ACL_LOG_INFO("reset event successfully.");
    ACL_ADD_RELEASE_SUCCESS_COUNT(acl::ACL_STATISTICS_RECORD_RESET_EVENT);
    return ACL_SUCCESS;
}

aclError aclrtQueryEvent(aclrtEvent event, aclrtEventStatus *status)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtQueryEvent);
    ACL_LOG_INFO("start to execute aclrtQueryEvent");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(event);
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(status);

    const rtError_t rtErr = rtEventQuery(static_cast<rtEvent_t>(event));
    if (rtErr == RT_ERROR_NONE) {
        *status = ACL_EVENT_STATUS_COMPLETE;
    } else if (rtErr == ACL_ERROR_RT_EVENT_NOT_COMPLETE) {
        *status = ACL_EVENT_STATUS_NOT_READY;
    } else {
        ACL_LOG_INNER_ERROR("query event status failed, runtime result = %d", static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    ACL_LOG_INFO("successfully execute aclrtQueryEvent");
    return ACL_SUCCESS;
}

aclError aclrtQueryEventStatus(aclrtEvent event, aclrtEventRecordedStatus *status)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtQueryEventStatus);
    ACL_LOG_INFO("start to execute aclrtQueryEventStatus");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(event);
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(status);

    rtEventStatus_t rtStatus = RT_EVENT_INIT;
    const rtError_t rtErr = rtEventQueryStatus(static_cast<rtEvent_t>(event), &rtStatus);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("[Query][Status]query event recorded status failed, runtime result = %d",
            static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    if (rtStatus == RT_EVENT_RECORDED) {
        *status = ACL_EVENT_RECORDED_STATUS_COMPLETE;
    } else {
        *status = ACL_EVENT_RECORDED_STATUS_NOT_READY;
    }
    ACL_LOG_INFO("successfully execute aclrtQueryEventStatus");
    return ACL_SUCCESS;
}

aclError aclrtQueryEventWaitStatus(aclrtEvent event, aclrtEventWaitStatus *status)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtQueryEventWaitStatus);
    ACL_LOG_INFO("start to execute aclrtQueryEventWaitStatus");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(event);
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(status);

    rtEventWaitStatus_t rtWaitStatus = EVENT_STATUS_NOT_READY;
    const rtError_t rtErr = rtEventQueryWaitStatus(static_cast<rtEvent_t>(event), &rtWaitStatus);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("[Query][Status]query event wait-status failed, runtime result = %d",
            static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    if (rtWaitStatus == EVENT_STATUS_COMPLETE) {
        *status = ACL_EVENT_WAIT_STATUS_COMPLETE;
    } else {
        *status = ACL_EVENT_WAIT_STATUS_NOT_READY;
    }
    ACL_LOG_INFO("successfully execute aclrtQueryEventWaitStatus");
    return ACL_SUCCESS;
}

aclError aclrtSynchronizeEvent(aclrtEvent event)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtSynchronizeEvent);
    ACL_LOG_INFO("start to execute aclrtSynchronizeEvent");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(event);

    const rtError_t rtErr = rtEventSynchronize(static_cast<rtEvent_t>(event));
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("wait event to be complete failed, runtime result = %d", static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    ACL_LOG_INFO("successfully execute aclrtSynchronizeEvent");
    return ACL_SUCCESS;
}

aclError aclrtSynchronizeEventWithTimeout(aclrtEvent event, int32_t timeout)
{
    ACL_LOG_INFO("start to execute aclrtSynchronizeEventWithTimeout, timeout = %dms", timeout);
    constexpr int32_t default_timeout = -1;
    if (timeout < default_timeout) {
        ACL_LOG_CALL_ERROR("the timeout of synchronize event is invalid");
        return ACL_ERROR_RT_PARAM_INVALID;
    }
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(event);

    const rtError_t rtErr = rtEventSynchronizeWithTimeout(static_cast<rtEvent_t>(event), timeout);
    if (rtErr == ACL_ERROR_RT_EVENT_SYNC_TIMEOUT) {
        ACL_LOG_CALL_ERROR("synchronize event timeout, timeout = %dms", timeout);
        return ACL_ERROR_RT_EVENT_SYNC_TIMEOUT;
    } else if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("wait event to be complete failed, runtime result = %dms", static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    ACL_LOG_INFO("successfully execute aclrtSynchronizeEventWithTimeout");
    return ACL_SUCCESS;
}

aclError aclrtEventElapsedTime(float *ms, aclrtEvent startEvent, aclrtEvent endEvent)
{
    ACL_LOG_INFO("start to execute aclrtEventElapsedTime");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(ms);
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(startEvent);
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(endEvent);

    const rtError_t rtErr = rtEventElapsedTime(ms, startEvent, endEvent);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("computes events elapsed time failed, runtime result = %d", static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    ACL_LOG_INFO("successfully execute aclrtEventElapsedTime");
    return ACL_SUCCESS;
}

aclError aclrtEventGetTimestamp(aclrtEvent event, uint64_t *timestamp)
{
    ACL_LOG_INFO("start to execute aclrtEventGetTimestamp");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(timestamp);
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(event);

    ACL_REQUIRES_CALL_RTS_OK(rtEventGetTimeStamp(timestamp, event), rtEventGetTimeStamp);

    ACL_LOG_INFO("successfully execute aclrtEventGetTimestamp");
    return ACL_SUCCESS;
}

aclError aclrtSetOpWaitTimeout(uint32_t timeout)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtSetOpWaitTimeout);
    ACL_LOG_INFO("start to execute aclrtSetOpWaitTimeout, timeout = %us", timeout);
    ACL_REQUIRES_CALL_RTS_OK(rtSetOpWaitTimeOut(timeout), rtSetOpWaitTimeOut);
    ACL_LOG_INFO("successfully execute aclrtSetOpWaitTimeout, timeout = %us", timeout);
    return ACL_SUCCESS;
}

aclError aclrtSetOpExecuteTimeOut(uint32_t timeout)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtSetOpExecuteTimeOut);
    ACL_LOG_INFO("start to execute aclrtSetOpExecuteTimeOut, timeout = %us", timeout);

    const rtError_t rtErr = rtSetOpExecuteTimeOut(timeout);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("set op execute timeout failed, runtime result = %d", static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }

    ACL_LOG_INFO("successfully execute aclrtSetOpExecuteTimeOut, timeout = %us", timeout);
    return ACL_SUCCESS;
}

aclError aclrtSetOpExecuteTimeOutWithMs(uint32_t timeout)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtSetOpExecuteTimeOutWithMs);
    ACL_LOG_INFO("start to execute aclrtSetOpExecuteTimeOutWithMs, timeout = %us", timeout);

    const rtError_t rtErr = rtSetOpExecuteTimeOutWithMs(timeout);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("set op execute timeout failed, runtime result = %d", static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }

    ACL_LOG_INFO("successfully execute aclrtSetOpExecuteTimeOutWithMs, timeout = %ums", timeout);
    return ACL_SUCCESS;
}

aclError aclrtGetMemUceInfo(int32_t deviceId, aclrtMemUceInfo *memUceInfoArray,
                            size_t arraySize, size_t *retSize)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtGetMemUceInfo);
    ACL_LOG_INFO("start to execute aclrtGetMemUceInfo on device %d, arraySize %zu", deviceId, arraySize);
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(memUceInfoArray);
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(retSize);

    rtMemUceInfo rtUceInfo = {};
    ACL_REQUIRES_CALL_RTS_OK(rtGetMemUceInfo(deviceId, &rtUceInfo), rtGetMemUceInfo);
    if (arraySize < rtUceInfo.count) {
        ACL_LOG_ERROR("failed to execute aclrtGetMemUceInfo, because arraySize %zu is less than the required size %u",
                      arraySize, rtUceInfo.count);
        return ACL_ERROR_INVALID_PARAM;
    }

    size_t realSize = std::min(rtUceInfo.count, RT_MAX_RECORD_PA_NUM_PER_DEV);
    for (size_t i = 0; i < realSize; ++i) {
        memUceInfoArray[i].addr = reinterpret_cast<void *>(rtUceInfo.repairAddr[i].ptr);
        memUceInfoArray[i].len = rtUceInfo.repairAddr[i].len;
        (void)memset_s(memUceInfoArray[i].reserved, sizeof(memUceInfoArray[i].reserved),
                       0, sizeof(memUceInfoArray[i].reserved));
    }
    *retSize = realSize;

    ACL_LOG_INFO("successfully execute aclrtGetMemUceInfo on device %d, arraySize %zu, retSize %zu,",
                 deviceId, arraySize, *retSize);
    return ACL_SUCCESS;
}

aclError aclrtMemUceRepair(int32_t deviceId, aclrtMemUceInfo *memUceInfoArray, size_t arraySize)
{
    ACL_PROFILING_REG(acl::AclProfType::AclrtMemUceRepair);
    ACL_LOG_INFO("start to execute aclrtMemUceRepair on device %d, arraySize %zu", deviceId, arraySize);
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(memUceInfoArray);

    if (arraySize > RT_MAX_RECORD_PA_NUM_PER_DEV) {
        ACL_LOG_ERROR("failed to execute aclrtMemUceRepair, because arraySize %zu is exceed max size %d",
                      arraySize, RT_MAX_RECORD_PA_NUM_PER_DEV);
        return ACL_ERROR_INVALID_PARAM;
    }

    //reserved value check, should be fill with zero
    size_t reservedZeroValue[UCE_INFO_RESERVED_SIZE] = {0};
    for (size_t i = 0; i < arraySize; ++i) {
        if (memcmp(memUceInfoArray[i].reserved, reservedZeroValue, UCE_INFO_RESERVED_SIZE)) {
            ACL_LOG_ERROR("failed to execute aclrtMemUceRepair with mismatched version, "
                          "pls set valid value only for ptr and len ");
            return ACL_ERROR_INVALID_PARAM;
        }
    }

    rtMemUceInfo rtUceInfo = {};
    rtUceInfo.devid = deviceId;
    rtUceInfo.count = arraySize;
    for (size_t i = 0; i < arraySize; ++i) {
        rtUceInfo.repairAddr[i].ptr = reinterpret_cast<uint64_t>(memUceInfoArray[i].addr);
        rtUceInfo.repairAddr[i].len = memUceInfoArray[i].len;
    }

    const rtError_t rtErr = rtMemUceRepair(deviceId, &rtUceInfo);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("rtMemUceRepair failed, device id is runtime result = %d", static_cast<int32_t>(rtErr));
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}
